package org.aerie.forest.core.frame.rebar.factory;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.aerie.forest.core.brick.exception.custom.runtime.ForestContainerException;
import org.aerie.forest.core.brick.exception.custom.runtime.ForestRuntimeException;
import org.aerie.forest.core.brick.exception.custom.runtime.ForestUnderdevelopmentException;
import org.aerie.forest.core.frame.rebar._shelf.ForestInitShelf;
import org.aerie.forest.core.frame.rebar.entity.AbstractForestRebar;
import org.aerie.forest.core.frame.rebar.entity.processer.exception.ExceptionProcessor;
import org.aerie.forest.core.frame.rebar.factory.ForestShelf.RebarScutcheon;

/**
 * @description forest容器管理器
 *
 * @author quark
 * @organization aerie
 * @date 2020年2月27日 下午3:45:45
 * @version 1.2.0
 */
public enum ForestContainer implements ForestFactoryComponent {

	/**
	 * @description 单例
	 * 
	 * @author quark
	 * @date 2020年2月27日 下午3:46:08
	 * @version 1.2.0
	 */
	INSTANCE;

	/**
	 * @description forest架构元素组
	 * 
	 * @author quark
	 * @date 2020年2月27日 下午3:46:19
	 * @version 1.2.0
	 */
	private Map<Class<? extends AbstractForestRebar>, ForestRebarContainer<? extends AbstractForestRebar>> forestRebarContainers = new HashMap<>();

	/**
	 * Construction
	 * 
	 *
	 * @date 2020年2月27日 下午3:46:31
	 * @version 1.2.0
	 */
	private ForestContainer() {
		// 组合forest架构元素容器【初始化】
		for (ForestRebarContainerCategory forestRebarContainerCategory : ForestRebarContainerCategory.values()) {
			forestRebarContainers.put(forestRebarContainerCategory.getZclass(),
					forestRebarContainerCategory.getForestRebarContainer());
		}
		LOGGER.debug(LogMsgPool.INIT_COMPLETE, "forest容器管理器");
	}

	/**
	 * @description 添加forest架构元素
	 * @param forestRebar 需要添加进容器的forest结构元素
	 * @throws Exception 添加异常
	 *
	 * @date 2020年2月27日 下午3:47:50
	 * @version 1.2.0
	 */
	public synchronized void addForestRebar(AbstractForestRebar forestRebar) throws Exception {
		for (ForestRebarContainerCategory forestRebarContainerCategory : ForestRebarContainerCategory.values()) {
			Class<? extends AbstractForestRebar> zclass = forestRebarContainerCategory.getZclass();
			if (!zclass.isInstance(forestRebar)) {
				continue;
			}
			// 获得对应类型的容器
			List<? extends AbstractForestRebar> container = forestRebarContainers.get(zclass).getContainer();
			if (container.stream().filter(p1 -> p1.hashCode() == forestRebar.hashCode()).count() == 0) {
				addForestRebarToList(container, forestRebar);
				LOGGER.debug("{}成功加入forest容器", forestRebar.getForestRebarName());
				return;
			} else {
				LOGGER.warn("forest容器中存在：【{}】，无法重复添加", forestRebar.getForestRebarName());
				return;
			}
		}
		throw new ForestContainerException("forest架构元素的容器分类缺少(" + forestRebar.getForestRebarName() + ")对应的类型，无法添加进容器");
	}

	/**
	 * @description 将对象强转称集合的类型并添加进去【内部调用方法，插入之前已经校验了类型可以强转】
	 * @param <T>         需要添加进容器的forest结构元素的类型
	 * @param list        添加至哪个容器
	 * @param forestRebar 需要添加进容器的forest结构元素
	 *
	 * @date 2020年2月27日 下午3:53:09
	 * @version 1.2.0
	 */
	@SuppressWarnings("unchecked")
	private <T extends AbstractForestRebar> void addForestRebarToList(List<T> list, AbstractForestRebar forestRebar) {
		list.add((T) forestRebar);
	}

	/**
	 * @description 从容器中取出对应的架构元素
	 * @param rebarScutcheon 货架名牌类别
	 * @return forest架构元素
	 *
	 * @date 2020年2月27日 下午3:54:40
	 * @version 1.2.0
	 */
	public AbstractForestRebar getForestRebar(RebarScutcheon<?> rebarScutcheon) {
		return getForestRebar(rebarScutcheon, true);
	}

	/**
	 * @description 从容器中取出对应的架构元素
	 * @param rebarScutcheon 货架名牌
	 * @param first          是否第一次尝试取出【仅第一次失败会尝试通过入库组建将对应的forest架构元素添加进容器】
	 * @return 需要取出来的forest架构元素
	 *
	 * @date 2020年2月27日 下午3:55:06
	 * @version 1.2.0
	 */
	private AbstractForestRebar getForestRebar(RebarScutcheon<?> rebarScutcheon, boolean first) {
		// 获得需要取出的架构元素所在的容器的类型
		Class<? extends AbstractForestRebar> zclassOfForestRebarContainer = getForestRebarContainerCategoryAuto(
				rebarScutcheon).getZclass();
		// 获得容器中存储对应架构元素的容器
		List<? extends AbstractForestRebar> forestRebarContainer = forestRebarContainers
				.get(zclassOfForestRebarContainer).getContainer();
		// 获得和要取得的架构元素基本类型相同的对象
		List<? extends AbstractForestRebar> collect = forestRebarContainer.stream()
				.filter(p1 -> p1.getClass() == rebarScutcheon.getForestRebarClass()).collect(Collectors.toList());
		// 容器中必须是单例
		if (collect == null || collect.size() > 1) {
			throw new ForestContainerException("forest容器单例模式被打破");
		}
		// 如果容器里面不存在元素则通过入库组件加入
		if (collect.isEmpty()) {
			if (!first) {
				// 第二次依然没有需要的forest架构元素则报错
				throw new ForestContainerException("无法从容器中取出对应的架构元素");
			}
			try {
				// 执行入库指令
				rebarScutcheon.getForestRebarStorage().putInStorage();
			} catch (Exception e) {
				if (rebarScutcheon.getForestRebarClass().equals(ExceptionProcessor.class)) {
					throw new ForestRuntimeException("exceptionPack崩溃 -> 请联系架构师") {

						/**
						 * @description 序列化编号
						 * 
						 * @author quark
						 * @date 2020-8-21 23:59:35
						 * @version 2.0.1
						 */
						private static final long serialVersionUID = 1L;
					};
				}
				ForestFactory.INSTANCE.getForestRebarFactory(ForestInitShelf.getInstance().EXCRPTION_PROCESSOR)
						.recordException("执行rebar入库指令失败", e);
			}
			return getForestRebar(rebarScutcheon, false);
		}
		return collect.get(0);
	}

	/**
	 * @description 自动获取架构元素对应的容器
	 * @return 对应的容器
	 *
	 * @date 2020年3月29日 下午2:05:34
	 * @version 1.2.0
	 */
	private ForestRebarContainerCategory getForestRebarContainerCategoryAuto(RebarScutcheon<?> rebarScutcheon) {
		Class<?> forestRebarClass = rebarScutcheon.getGenericityZclass();
		for (ForestRebarContainerCategory forestRebarContainerCategory : ForestRebarContainerCategory.values()) {
			if (forestRebarContainerCategory.getZclass().isAssignableFrom(forestRebarClass)) {
				return forestRebarContainerCategory;
			}
		}
		throw new ForestUnderdevelopmentException("forest架构元素的容器:" + forestRebarClass.getSimpleName());
	}
}
